/*
 * Copyright (C) 2010 mAPPn.Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mol.market.common.util;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.util.EntityUtils;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.LayoutAnimationController;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;

import com.mol.market.Constants;
import com.mol.market.R;
import com.mol.market.Session;
import com.mol.market.common.download.DownloadManager.Impl;
import com.mol.market.common.download.DownloadNotification;
import com.mol.market.common.download.Helper;
import com.mol.market.common.network.AndroidHttpClient;
import com.mol.market.common.network.MarketAPI;
import com.mol.market.common.vo.RecommendTopic;
import com.mol.market.common.widget.DefaultExceptionHandler;
import com.mol.market.ui.LoginActivity;
import com.mol.market.ui.RecommendActivity;
import com.mol.market.ui.SplashActivity;
import com.mol.market.ui_v4.ProductDetailActivity;

/**
 * Common Utils for the application
 * 
 * @author andrew.wang
 * @date 2010-9-19
 * @since Version 0.4.0
 */
public class Utils {

  public static boolean sDebug;
  public static String sLogTag;

  private static final String TAG = "Utils";

  // UTF-8 encoding
  public static final String ENCODING_UTF8 = "UTF-8";

  private static WeakReference<Calendar> calendar;

  /**
   * <p>
   * Get UTF8 bytes from a string
   * </p>
   * 
   * @param string
   *          String
   * @return UTF8 byte array, or null if failed to get UTF8 byte array
   */
  public static byte[] getUTF8Bytes(String string) {
    if (string == null)
      return new byte[0];

    try {
      return string.getBytes(ENCODING_UTF8);
    } catch (UnsupportedEncodingException e) {
      /*
       * If system doesn't support UTF-8, use another way
       */
      try {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        dos.writeUTF(string);
        byte[] jdata = bos.toByteArray();
        bos.close();
        dos.close();
        byte[] buff = new byte[jdata.length - 2];
        System.arraycopy(jdata, 2, buff, 0, buff.length);
        return buff;
      } catch (IOException ex) {
        return new byte[0];
      }
    }
  }

  /**
   * <p>
   * Get string in UTF-8 encoding
   * </p>
   * 
   * @param b
   *          byte array
   * @return string in utf-8 encoding, or empty if the byte array is not encoded
   *         with UTF-8
   */
  public static String getUTF8String(byte[] b) {
    if (b == null)
      return "";
    return getUTF8String(b, 0, b.length);
  }

  /**
   * <p>
   * Get string in UTF-8 encoding
   * </p>
   */
  public static String getUTF8String(byte[] b, int start, int length) {
    if (b == null) {
      return "";
    } else {
      try {
        return new String(b, start, length, ENCODING_UTF8);
      } catch (UnsupportedEncodingException e) {
        return "";
      }
    }
  }

  /**
   * <p>
   * Parse int value from string
   * </p>
   * 
   * @param value
   *          string
   * @return int value
   */
  public static int getInt(String value) {
    if (TextUtils.isEmpty(value)) {
      return 0;
    }

    try {
      return Integer.parseInt(value.trim(), 10);
    } catch (NumberFormatException e) {
      return 0;
    }
  }

  /**
   * <p>
   * Parse float value from string
   * </p>
   * 
   * @param value
   *          string
   * @return float value
   */
  public static float getFloat(String value) {
    if (value == null)
      return 0f;

    try {
      return Float.parseFloat(value.trim());
    } catch (NumberFormatException e) {
      return 0f;
    }
  }

  /**
   * <p>
   * Parse long value from string
   * </p>
   * 
   * @param value
   *          string
   * @return long value
   */
  public static long getLong(String value) {
    if (value == null)
      return 0L;

    try {
      return Long.parseLong(value.trim());
    } catch (NumberFormatException e) {
      return 0L;
    }
  }

  public static void V(String msg) {
    if (sDebug) {
      Log.v(sLogTag, msg);
    }
  }

  public static void V(String msg, Throwable e) {
    if (sDebug) {
      Log.v(sLogTag, msg, e);
    }
  }

  public static void D(String msg) {
    if (sDebug) {
      Log.d(sLogTag, msg);
    }
  }

  public static void D(String msg, Throwable e) {
    if (sDebug) {
      Log.d(sLogTag, msg, e);
    }
  }

  public static void I(String msg) {
    if (sDebug) {
      Log.i(sLogTag, msg);
    }
  }

  public static void I(String msg, Throwable e) {
    if (sDebug) {
      Log.i(sLogTag, msg, e);
    }
  }

  public static void W(String msg) {
    if (sDebug) {
      Log.w(sLogTag, msg);
    }
  }

  public static void W(String msg, Throwable e) {
    if (sDebug) {
      Log.w(sLogTag, msg, e);
    }
  }

  public static void E(String msg) {
    if (sDebug) {
      Log.e(sLogTag, msg);
    }
  }

  public static void E(String msg, Throwable e) {
    if (sDebug) {
      Log.e(sLogTag, msg, e);
    }
  }

  public static String formatDate(long time) {
    if (calendar == null || calendar.get() == null) {
      calendar = new WeakReference<Calendar>(Calendar.getInstance());
    }
    Calendar target = calendar.get();
    target.setTimeInMillis(time);
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    return sdf.format(target.getTime());
  }

  public static String getTodayDate() {
    if (calendar == null || calendar.get() == null) {
      calendar = new WeakReference<Calendar>(Calendar.getInstance());
    }
    Calendar today = calendar.get();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    return sdf.format(today.getTime());
  }

  /**
   * Returns whether the network is available
   */
  public static boolean isNetworkAvailable(Context context) {

    if (context == null) {
      return false;
    }

    ConnectivityManager connectivity = (ConnectivityManager) context
        .getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivity == null) {
      Log.w(TAG, "couldn't get connectivity manager");
    } else {
      NetworkInfo[] info = connectivity.getAllNetworkInfo();
      if (info != null) {
        for (int i = 0, length = info.length; i < length; i++) {
          if (info[i].getState() == NetworkInfo.State.CONNECTED) {
            return true;
          }
        }
      }
    }
    return false;
  }

  /**
   * Returns whether the network is mobile
   */
  public static boolean isMobileNetwork(Context context) {
    ConnectivityManager connectivity = (ConnectivityManager) context
        .getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivity == null) {
      // Log.w(Constants.TAG, "couldn't get connectivity manager");
    } else {
      NetworkInfo info = connectivity.getActiveNetworkInfo();
      if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE) {
        return true;
      }
    }
    return false;
  }

  /**
   * 获取用户客户端的运营商名
   */
  public static String getCarrier(Context context) {
    Cursor mCursor = context.getContentResolver().query(
        Uri.parse("content://telephony/carriers"), new String[] { "apn" },
        "current=1", null, null);
    if (mCursor != null) {
      try {
        if (mCursor.moveToFirst()) {
          return mCursor.getString(0);
        }
      } catch (Exception e) {
        Utils.E("Can not get Network info", e);
      } finally {
        mCursor.close();
      }
    }
    return "";
  }

  /**
   * 格式化时间（Format：yyyy-MM-dd HH:mm）
   * 
   * @param timeInMillis
   * @return
   */
  public static String formatTime(long timeInMillis) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    return sdf.format(new Date(timeInMillis));
  }

  /**
   * 文件拷贝工具类
   * 
   * @param in
   *          源文件
   * @param dst
   *          目标文件
   * @throws IOException
   */
  public static void copyFile(InputStream in, FileOutputStream dst)
      throws IOException {
    byte[] buffer = new byte[8192];
    int len = 0;
    while ((len = in.read(buffer)) > 0) {
      dst.write(buffer, 0, len);
    }
    in.close();
    dst.close();
  }

  /**
   * 解析HTTP String Entity
   * 
   * @param response
   *          HTTP Response
   * @return 市场API返回的消息(String)
   */
  public static String getStringResponse(HttpResponse response) {
    HttpEntity entity = response.getEntity();
    try {
      return entity == null ? null : EntityUtils.toString(response.getEntity());
    } catch (ParseException e) {
      D("getStringResponse meet ParseException", e);
    } catch (IOException e) {
      D("getStringResponse meet IOException", e);
    }
    return null;
  }

  /**
   * 解析HTTP String Entity
   * 
   * @param response
   *          HTTP Response
   * @return 市场API返回的消息(String)
   */
  public static String getGzipStringResponse(HttpResponse response) {
    HttpEntity entity = response.getEntity();
    InputStream is = null;
    try {
      if (entity == null)
        return null;
      is = AndroidHttpClient.getUngzippedContent(entity);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      copyStreamInner(is, baos);
      return new String(baos.toByteArray(), "utf8");
    } catch (ParseException e) {
      D("getStringResponse meet ParseException", e);
    } catch (IOException e) {
      D("getStringResponse meet IOException", e);
    } finally {
      if (is != null) {
        try {
          is.close();
        } catch (IOException e) {
        }
      }
    }
    return null;
  }

  /**
   * 流复制
   * 
   * @param in
   * @param out
   * @throws IOException
   */
  public static void copyStreamInner(InputStream in, OutputStream out)
      throws IOException {
    byte[] buff = new byte[4096];
    int length = 0;
    while ((length = in.read(buff)) >= 0) {
      out.write(buff, 0, length);
      out.flush();
    }
  }

  /**
   * 解析HTTP InputStream Entity
   * 
   * @param response
   *          HTTP Response
   * @return 市场API返回的消息(InputStream)
   */
  public static InputStream getInputStreamResponse(HttpResponse response) {
    HttpEntity entity = response.getEntity();
    try {
      if (entity == null)
        return null;
      return AndroidHttpClient.getUngzippedContent(entity);
    } catch (IllegalStateException e) {
      D("getInputStreamResponse meet IllegalStateException", e);
    } catch (IOException e) {
      D("getInputStreamResponse meet IOException", e);
    }
    return null;
  }

  /**
   * 界面切换动画
   * 
   * @return
   */
  public static LayoutAnimationController getLayoutAnimation() {
    AnimationSet set = new AnimationSet(true);

    Animation animation = new AlphaAnimation(0.0f, 1.0f);
    animation.setDuration(50);
    set.addAnimation(animation);

    animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
        Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
        Animation.RELATIVE_TO_SELF, 0.0f);
    animation.setDuration(100);
    set.addAnimation(animation);

    LayoutAnimationController controller = new LayoutAnimationController(set,
        0.5f);
    return controller;
  }

  /**
   * 创建Tab中的只包含TextView的View
   */
  public static View createTabView(Context context, Session session,
      String text, int index, TextView v) {

    TextView view;
    if (v == null) {
      view = (TextView) LayoutInflater.from(context).inflate(
          R.layout.common_tab_view, null);
    } else {
      view = v;
    }
    
    baseIndexSetParams(index, context, session, view, text, view);

    return view;
  }
  
    /**
     * <br>创建一个Tab，其中包含动画。</br>
     * TextView_ID --> R.id.tv_tab.
     */
  public static View createTabWithAnimation(Context context, Session session, 
          String text, int index,AnimationDrawable animation) {
      View ancestor = (View) LayoutInflater.from(context).inflate(R.layout.tab_view_animation,
              null);

      TextView tv = (TextView) ancestor.findViewById(R.id.tv_tab);
      
      baseIndexSetParams(index, context, session, tv, text, ancestor);

      ImageView iv = (ImageView) ancestor.findViewById(R.id.iv_animation);
      iv.setBackgroundDrawable(animation);
      return ancestor;
  }
  
  /**
   * @param index    left(-1)，mid(0)，right(1)
   * @param context 
   * @param session
   * @param tv       显示文字的TextView
   * @param text     设置的文字
   * @param ancestor 所处的ViewGroup中，若没有，用tv代替。
   * {@link #createTabView(Context, Session, String, int, TextView)}
   * {@link #createTabWithAnimation(Context, Session, String, int, AnimationDrawable)}
   */
  private static void baseIndexSetParams(int index,Context context,
          Session session,TextView tv,String text,View ancestor){
      
      tv.setTextAppearance(context, ThemeManager.getResource(session, ThemeManager.KEY_TAB_TEXT));
      tv.setText(text);
      
      setTabBackground(index, session, ancestor);
  }
  
  /**
   * 根据主题设置Tab的背景
   * @param index   left(-1)，mid(0)，right(1)
   * @param session
   * @param target
   */
  private static void setTabBackground(int index, Session session, View target) {
      if (index == -1) {
          target.setBackgroundResource(ThemeManager.getResource(session, ThemeManager.KEY_TAB_L));
      } else if (index == 1) {
          target.setBackgroundResource(ThemeManager.getResource(session, ThemeManager.KEY_TAB_R));
      } else {
          target.setBackgroundResource(ThemeManager.getResource(session, ThemeManager.KEY_TAB_M));
      }
  }

  /**
   * Tab换肤
   * @param activity 
   * @param session
   * @param textViewId 如果Tab中不仅仅是TextView，且包含TextView，就传入TextView的Id
   * @return Success or failure
   */
  public static boolean tabInitSkin(Activity activity,Session session,int textViewId){
      
      View tabHost = activity.findViewById(android.R.id.tabhost);
      
      if(tabHost == null || !(tabHost instanceof TabHost)){
          return false;
      }
      TabHost mTabHost = (TabHost) tabHost;
      final int tabCount = mTabHost.getTabWidget().getChildCount();
      //tabCount 两种情况：
      //1.等于2的时候，应该为-1，1.
      //2.大于2的时候，应该为-1，0... ，1.
      //[ tab(-1) tab(0) tab(0) tab(0) tab(1) ]
      for (int i = 0; i < tabCount; i++) {
          View v = mTabHost.getTabWidget().getChildTabViewAt(i);
          
          //If it is TextView, need to change the font color
          if(v instanceof TextView){
              ((TextView) v).setTextAppearance(activity,
                      ThemeManager.getResource(session, ThemeManager.KEY_TAB_TEXT));
          }else{
              //Trying to find
              View textView = v.findViewById(textViewId);
              if(textView != null){
                  ((TextView) textView).setTextAppearance(activity,
                          ThemeManager.getResource(session, ThemeManager.KEY_TAB_TEXT));
              }
          }
          
          //No matter be what kind of View, always to be like this.
          setTabBackground(getTabIndex(i, tabCount), session, v);
      }
      return true;
  }
  
  /**
   * Tab换肤
   * @param activity   
   * @param session
   * @return
   */
  public static boolean tabInitSkin(Activity activity,Session session){
      //default id
      return tabInitSkin(activity,session,R.id.tv_tab);
  }
  
  /**
   * <br>得到Tab的位置，也就是所谓的Index.</br>
   * Used in {@link setTabBackground()}.
   * @param index
   * @param count
   * @return Success or failure
   */
  private static int getTabIndex(int index,int count){
      if(index == 0){
          return -1;
      }else if(index == count - 1){
          return 1;
      }else{
          return 0;
      }
  }

  public static HashMap<String, Object> getAppInfo(Context ctx, String pkgName) {
      PackageManager pm = ctx.getPackageManager();
      try {
          HashMap<String, Object> item = new HashMap<String, Object>();
          PackageInfo info = pm.getPackageInfo(pkgName, 0);

          item.put(Constants.KEY_PRODUCT_PACKAGE_NAME, info.packageName);
          item.put(Constants.KEY_PRODUCT_ICON_URL_LDPI, info);
          item.put(Constants.KEY_PRODUCT_NAME,
                  info.applicationInfo.loadLabel(pm));
          item.put(Constants.KEY_PRODUCT_VERSION_NAME,
                  ctx.getString(R.string.current_version, info.versionName));
          return item;
      } catch (NameNotFoundException e) {
          e.printStackTrace();
      }
      return null;
  }

  /**
   * 获取用户安装的应用列表
   */
  public static List<PackageInfo> getInstalledApps(final Context context) {
    PackageManager pm = context.getPackageManager();
    final String ourPackageName = Session.get(context).getPackageName();
    List<PackageInfo> packages = pm.getInstalledPackages(0);
    final List<PackageInfo> apps = new ArrayList<PackageInfo>();
    HashSet<String> installed = new HashSet<String>();
    for (PackageInfo info : packages) {
      // 只返回非系统级应用
      if (!ourPackageName.equals(info.packageName)) {
        apps.add(info);
        installed.add(info.packageName);
      }
    }
    new Thread() {
      public void run() {
        final PackageManager pm = context.getPackageManager();
          HashMap<String, HashMap<String, Object>> data =
                  new HashMap<String, HashMap<String, Object>>();
        for (PackageInfo info : apps) {
          if (isSystemApp(info)) {
            continue;
          }
          HashMap<String, Object> item = new HashMap<String, Object>();

          item.put(Constants.KEY_PRODUCT_PACKAGE_NAME, info.packageName);
          item.put(Constants.KEY_PRODUCT_ICON_URL_LDPI, info);
          item.put(Constants.KEY_PRODUCT_NAME,
              info.applicationInfo.loadLabel(pm));
          item.put(Constants.KEY_PRODUCT_VERSION_NAME,
              context.getString(R.string.current_version, info.versionName));
          item.put(Constants.KEY_PRODUCT_VERSION_CODE, info.versionCode);
          item.put(Constants.KEY_PRODUCT_RSA_MD5, StringUtils.toHexString(
                  getFileSignatureMd5(info.applicationInfo.sourceDir), false));
          data.put(info.packageName, item);
        }
        Session.get(context).mPreloadInstalled = data;
      }
    }.start();
    Session.get(context).setInstalledApps(installed);
    return apps;
  }

  /**
   * 判断应用是否系统级应用
   */
  public static boolean isSystemApp(PackageInfo info) {
    return (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
  }

  public static PackageInfo getPackageInfo(Context context,
      String packageName) {
    PackageManager pm = context.getPackageManager();
    try {
      return pm.getPackageInfo(packageName, 0);
    } catch (NameNotFoundException e) {
      Utils.E("NameNotFoundException", e);
    }
    return null;
  }

  /**
   * 获取用户本机所有应用程序
   */
  public static List<PackageInfo> getAllInstalledApps(Context context) {
    PackageManager pm = context.getPackageManager();
    List<PackageInfo> packages = pm.getInstalledPackages(0);
    List<PackageInfo> apps = new ArrayList<PackageInfo>();
    HashSet<String> installed = new HashSet<String>();
    for (PackageInfo info : packages) {
      if ("com.mol.market".equals(info.packageName)) {
        continue;
      }
      apps.add(info);
      installed.add(info.packageName);
    }
    Session.get(context).setInstalledApps(installed);
    return apps;
  }

  /**
   * 判断签名是否一致
   */
  public static boolean isSameSign(Context context, String packageName,
      String oldSignature) {

    byte[] signL = StringUtils.fromHexString(oldSignature);

    String path = getPathWithPackageName(context, packageName);
    if (!TextUtils.isEmpty(path)) {
      byte[] signR = getFileSignatureMd5(path);
      return isEqual(signL, signR);
    }
    return true;
  }

  /**
   * 通过包名获取文件路径
   */
  public static String getPathWithPackageName(Context context,
      String packageName) {
    PackageManager pm = context.getPackageManager();
    PackageInfo info = null;
    try {
      info = pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
    } catch (NameNotFoundException e) {
      e.printStackTrace();
    }
    if (info == null) {
      return "";
    }
    return info.applicationInfo.publicSourceDir;
  }

  public static boolean compareFileWithPathAndPkg(Context context,
      String path1, String pkgName) {
    String path2 = getPathWithPackageName(context, pkgName);

    if (TextUtils.isEmpty(path2)) {
      // 此应用没有被安装
      return true;
    }

    return compareFileWithSignature(path1, path2);
  }

  /**
   * 比较两个文件的签名是否一致
   */
  public static boolean compareFileWithSignature(String path1, String path2) {

    long start = System.currentTimeMillis();
    if (TextUtils.isEmpty(path1) || TextUtils.isEmpty(path2)) {
      return false;
    }

    byte[] signature1 = getFileSignatureMd5(path1);
    byte[] signature2 = getFileSignatureMd5(path2);

    V("compareFileWithSignature total time is "
        + (System.currentTimeMillis() - start));
    return isEqual(signature1, signature2);
  }

  /**
   * 获取应用签名MD5
   */
  public static byte[] getFileSignatureMd5(String targetFile) {
    try {
      JarFile jarFile = new JarFile(targetFile);
      // 取RSA公钥
      JarEntry jarEntry = jarFile.getJarEntry("AndroidManifest.xml");

      if (jarEntry != null) {
        InputStream is = jarFile.getInputStream(jarEntry);
        byte[] buffer = new byte[8192];
        while (is.read(buffer) > 0) {
          // do nothing
        }
        is.close();
        Certificate[] certs = jarEntry.getCertificates();
        if (certs != null && certs.length > 0) {
          byte[] encode = null;
          try {
            encode = certs[0].getEncoded();
          } catch (CertificateEncodingException e) {
          }
          return getMd5(encode);
        }
      }
    } catch (IOException e) {
      W("occur IOException when get file signature", e);
    } catch (Exception ex) {
      // 2011/8/26 Fix OFB Exception
      W("occur other Exception when get file signature", ex);
    }
    return null;
  }

  /**
   * Get MD5 Code
   */
  public static String getMD5(String text) {
    try {
      byte[] byteArray = text.getBytes("utf8");
      MessageDigest md = MessageDigest.getInstance("MD5");
      md.update(byteArray, 0, byteArray.length);
      return StringUtils.toHexString(md.digest(), false);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
    return "";
  }

  /**
   * Check whether the SD card is readable
   */
  public static boolean isSdcardReadable() {
    final String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)
        || Environment.MEDIA_MOUNTED.equals(state)) {
      return true;
    }
    return false;
  }

  /**
   * Check whether the SD card is writable
   */
  public static boolean isSdcardWritable() {
    final String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
      return true;
    }
    return false;
  }

  /**
   * Show toast information
   * 
   * @param context
   *          application context
   * @param text
   *          the information which you want to show
   * @return show toast dialog
   */
  public static void makeEventToast(Context context, String text,
      boolean isLongToast) {

    Toast toast;
    if (isLongToast) {
      toast = Toast.makeText(context, "", Toast.LENGTH_LONG);
    } else {
      toast = Toast.makeText(context, "", Toast.LENGTH_SHORT);
    }
    View v = LayoutInflater.from(context).inflate(R.layout.toast_view, null);
    TextView textView = (TextView) v.findViewById(R.id.text);
    textView.setText(text);
    toast.setView(v);
    toast.show();
  }

  /**
   * 解析二维码地址
   */
  public static HashMap<String, String> parserUri(Uri uri) {
    HashMap<String, String> parameters = new HashMap<String, String>();
    String paras[] = uri.getQuery().split("&");
    for (String s : paras) {
      if (s.contains("=")) {
        String[] item = s.split("=");
        parameters.put(item[0], item[1]);
      } else {
        return null;
      }
    }
    return parameters;
  }

  /**
   * 检查默认Proxy
   */
  public static HttpHost detectProxy(Context context) {
    ConnectivityManager cm = (ConnectivityManager) context
        .getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo ni = cm.getActiveNetworkInfo();
    if (ni != null && ni.isAvailable()
        && ni.getType() == ConnectivityManager.TYPE_MOBILE) {
      String proxyHost = android.net.Proxy.getDefaultHost();
      int port = android.net.Proxy.getDefaultPort();
      if (proxyHost != null) {
        return new HttpHost(proxyHost, port, "http");
      }
    }
    return null;
  }

  /**
   * 检查文件是否存在
   */
  public static boolean isFileExist(String path) {
    if (TextUtils.isEmpty(path)) {
      return false;
    }
    return new File(path).exists();
  }

  /**
   * Android 安装应用
   * 
   * @param context
   *          Application Context
   * @param file
   *          APK文件
   */
  public static void installApk(Context context, File file) {
    if (file.exists()) {
      Intent i = new Intent(Intent.ACTION_VIEW);
      i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      i.setDataAndType(Uri.fromFile(file),
          "application/vnd.android.package-archive");
      try {
        context.startActivity(i);
      } catch (ActivityNotFoundException e) {
      }

    } else {
      makeEventToast(context,
          context.getString(R.string.install_fail_file_not_exist), false);
    }
  }

  /**
   * 打开一个应用
   */
  public static void openApk(Context context, String pkgName) {
    PackageManager manager = context.getPackageManager();
    Intent i = manager.getLaunchIntentForPackage(pkgName);
    if (i == null) {
      // FIX 2011/11/2 can not get the intent object
      makeEventToast(context, context.getString(R.string.alert_open_apk_error),
          false);
      return;
    }
    try {
      context.startActivity(i);
    } catch (ActivityNotFoundException e) {
      makeEventToast(context, context.getString(R.string.alert_open_apk_error),
          false);
    }
  }

  /**
   * 卸载应用
   * 
   * @param context
   *          应用上下文
   * @param pkgName
   *          包名
   */
  public static void uninstallApk(Context context, String pkgName) {
    Uri packageURI = Uri.parse("package:" + pkgName);
    Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
    uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(uninstallIntent);
  }

  /**
   * 获取机锋市场下载的应用文件
   */
  public static ArrayList<HashMap<String, Object>> getLocalApks(Context context) {
    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
      File root = new File(Environment.getExternalStorageDirectory(),
          Constants.ROOT_DIR);
      ArrayList<HashMap<String, Object>> apks = new ArrayList<HashMap<String, Object>>();
      getApkList(context, root, apks);
      return apks;
    }
    return null;
  }

  /*
   * 遍历Gfan APK文件
   */
  private static void getApkList(Context context, File root,
      ArrayList<HashMap<String, Object>> apkList) {

    int index = 0;
    File marketRoot = new File(root, "market");
    boolean hasMarket = false;
    if (marketRoot.exists()) {
      File[] children = marketRoot.listFiles();
      if (children.length > 0) {

        for (File child : children) {
          if (!child.isDirectory()) {
            if (child.getName().endsWith(".apk")) {
              HashMap<String, Object> item = getApkInfo(context, child);
              if (item != null) {
                index++;
                apkList.add(item);
              }
            }
          }
        }
        if (index > 0) {
          hasMarket = true;
          HashMap<String, Object> group = new HashMap<String, Object>();
          group.put(
              Constants.KEY_PRODUCT_NAME,
              context.getString(R.string.apk_title_market) + "("
                  + marketRoot.getAbsolutePath() + ")");
          group.put(Constants.KEY_PLACEHOLDER, true);
          apkList.add(0, group);
        }
      }
    }

    File bbsRoot = new File(root, "bbs");
    boolean hasBbs = false;
    if (bbsRoot.exists()) {
      File[] children = bbsRoot.listFiles();
      if (children.length > 0) {

        int startPos = index;
        for (File child : children) {
          if (!child.isDirectory()) {
            if (child.getName().endsWith(".apk")) {
              HashMap<String, Object> item = getApkInfo(context, child);
              if (item != null) {
                index++;
                apkList.add(item);
              }
            }
          }
        }

        if (index > startPos) {
          hasBbs = true;
          HashMap<String, Object> group = new HashMap<String, Object>();
          group.put(
              Constants.KEY_PRODUCT_NAME,
              context.getString(R.string.apk_title_bbs) + "("
                  + bbsRoot.getAbsolutePath() + ")");
          group.put(Constants.KEY_PLACEHOLDER, true);
          if (hasMarket) {
            apkList.add(startPos + 1, group);
          } else {
            apkList.add(startPos, group);
          }
        }
      }
    }

    File cloudRoot = new File(root, "cloud");
    if (cloudRoot.exists()) {
      File[] children = cloudRoot.listFiles();
      if (children.length > 0) {

        int startPos = index;
        for (File child : children) {
          if (!child.isDirectory()) {
            if (child.getName().endsWith(".apk")) {
              HashMap<String, Object> item = getApkInfo(context, child);
              if (item != null) {
                index++;
                apkList.add(item);
              }
            }
          }
        }

        if (index > startPos) {
          HashMap<String, Object> group = new HashMap<String, Object>();
          group.put(
              Constants.KEY_PRODUCT_NAME,
              context.getString(R.string.apk_title_cloud) + "("
                  + cloudRoot.getAbsolutePath() + ")");
          group.put(Constants.KEY_PLACEHOLDER, true);
          if (hasMarket && hasBbs) {
            apkList.add(startPos + 2, group);
          } else if (hasMarket | hasBbs) {
            apkList.add(startPos + 1, group);
          } else {
            apkList.add(startPos, group);
          }
        }
      }
    }
  }

  /**
   * 获取APK信息
   */
  public static HashMap<String, Object> getApkInfo(Context context, File file) {
    PackageManager pm = context.getPackageManager();
    String filePath = file.getAbsolutePath();
    PackageInfo info = pm.getPackageArchiveInfo(filePath,
        PackageManager.GET_ACTIVITIES);
    if (info == null) {
      return null;
    }
    ApplicationInfo appInfo = info.applicationInfo;
    info.applicationInfo.sourceDir = filePath;
    info.applicationInfo.publicSourceDir = filePath;
    Drawable icon = pm.getApplicationIcon(appInfo);

    HashMap<String, Object> apk = new HashMap<String, Object>();
    apk.put(Constants.KEY_PRODUCT_ICON, icon);
    apk.put(Constants.KEY_PRODUCT_NAME, file.getName());
    apk.put(Constants.KEY_PRODUCT_INFO, filePath);
    apk.put(Constants.KEY_PRODUCT_DESCRIPTION, file.getAbsolutePath());
    apk.put(Constants.KEY_PRODUCT_PAY_TYPE, Constants.PAY_TYPE_FREE);
    apk.put(Constants.KEY_PLACEHOLDER, false);
    return apk;
  }

  /**
   * 获取APK文件的包名
   */
  public static String getPackageName(Context context, String filePath) {

    if (TextUtils.isEmpty(filePath)) {
      return "";
    }

    PackageManager pm = context.getPackageManager();
    PackageInfo info = pm.getPackageArchiveInfo(filePath,
        PackageManager.GET_ACTIVITIES);
    if (info == null) {
      return "";
    }
    return info.packageName;
  }

  /**
   * 删除安装包
   */
  public static boolean deleteFile(String file) {

    if (TextUtils.isEmpty(file)) {
      return false;
    }

    File realFile = new File(file);
    return realFile.delete();
  }

  /**
   * 计算下载进度字符串
   */
  public static String calculateRemainBytes(Context ctx, float current,
      float total) {

    float remain = total - current;
    remain = remain > 0 ? remain : 0;

    if (remain == 0) {
      return "";
    }
    String text = "";
    final String megaBytes = "M";
    final String kiloBytes = "K";
    final String bytes = "B";
    if (remain > 1000000) {
      text = ctx.getString(R.string.download_remain_bytes,
          String.format("%.02f", (remain / 1000000)), megaBytes);
    } else if (remain > 1000) {
      text = ctx.getString(R.string.download_remain_bytes,
          String.format("%.02f", (remain / 1000)), kiloBytes);
    } else {
      text = ctx.getString(R.string.download_remain_bytes, (int) remain, bytes);
    }
    return text;
  }

  /**
   * 检查是否应该进行更新
   */
  public static boolean isNeedCheckUpgrade(Context context) {
    long currentTime = System.currentTimeMillis();
    long lastCheckTime = Session.get(context).getUpdataCheckTime();
    if (currentTime - lastCheckTime > 86400000) {
      // we only check update every 24 hours
      return true;
    }
    return false;
  }

  /**
   * 统计工具方法
   */
  public static void trackEvent(Context context, String... paras) {
    if (paras == null || paras.length != 2) {
      return;
    }
//    MobclickAgent.onEvent(context, paras[0], paras[1]);
//    TCAgent.onEvent(context, paras[0], paras[1]);
//    makeEventToast(context, paras[0] + paras[1], false);
    // no analysis method
  }

  public static String submitLogs() {
    Process mLogcatProc = null;
    BufferedReader reader = null;
    try {
      mLogcatProc = Runtime.getRuntime().exec(
          new String[] { "logcat", "-d", "机锋市场:v" });

      reader = new BufferedReader(new InputStreamReader(
          mLogcatProc.getInputStream()));

      String line;
      final StringBuilder log = new StringBuilder();
      String separator = System.getProperty("line.separator");

      while ((line = reader.readLine()) != null) {
        log.append(line);
        log.append(separator);
      }
      return log.toString();

      // do whatever you want with the log. I'd recommend using Intents to
      // create an email
    } catch (IOException e) {
    } finally {
      if (reader != null)
        try {
          reader.close();
        } catch (IOException e) {
        }
    }
    return "";
  }

  /**
   * 获取用户的指纹信息
   */
  public static String getFingerPrint(Context context) {
    Session session = Session.get(context);
    return getMD5(session.getIMEI() + session.getMac());
  }

  public static void clearCache(Context context) {
    File file = Environment.getDownloadCacheDirectory();
    File[] files = file.listFiles();
    if (files != null) {
      for (File f : files) {
        f.delete();
      }
    }
    file = context.getCacheDir();
    files = file.listFiles();
    if (files != null) {
      for (File f : files) {
        f.delete();
      }
    }
  }

  public static boolean isEqual(byte[] a, byte[] b) {
    if (a == null || b == null) {
      return a == b;
    } else if (a.length != b.length) {
      return false;
    } else {
      for (int i = 0; i < b.length; i++) {
        if (a[i] != b[i]) {
          return false;
        }
      }
      return true;
    }
  }

  /**
   * 计算md5值
   */
  public static byte[] getMd5(String str) {
    if (str == null) {
      return null;
    }
    byte[] result = null;
    try {
      result = getMd5(str.getBytes("UTF-8"));
    } catch (UnsupportedEncodingException e) {
    }
    return result;
  }

  /**
   * 计算md5值
   */
  public static byte[] getMd5(byte[] bytes) {
    if (bytes == null) {
      return null;
    }
    StreamUtil su = new StreamUtil(true);
    try {
      su.copyStreamInner(new ByteArrayInputStream(bytes), null);
    } catch (IOException e) {
    }
    return su.getMD5();
  }

  /**
   * 收集错误日志
   * 
   * @param context
   */
  public static void onError(Context context) {
    UncaughtExceptionHandler currentHandler = Thread
        .getDefaultUncaughtExceptionHandler();
    // don't register again if already registered
    if (!(currentHandler instanceof DefaultExceptionHandler)) {
      // Register default exceptions handler
      Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler(
          currentHandler, context));

    }
  }

  public static String checkMarketFile(Context context) {

    SharedPreferences preference = PreferenceManager
        .getDefaultSharedPreferences(context);
    long id = preference.getLong(Session.P_UPDATE_ID, -1);
    if (id == -1) {
      return "";
    }

    Cursor c = context.getContentResolver().query(Impl.CONTENT_URI, null,
        DownloadNotification.PUBLIC_WHERE_OTA_COMPLETED, null, null);

    if (c != null) {
      String fileName = "";
      if (c.moveToFirst()) {
        if (c.getInt(c.getColumnIndex(Impl.COLUMN_STATUS)) == Impl.STATUS_SUCCESS) {
          fileName = c.getString(c.getColumnIndex(Impl.COLUMN_DATA));
        }
      }
      c.close();
      return fileName;
    } else {
      return "";
    }
  }

  public static boolean copyMarketFile(Context context, String in, String out) {

    if (!new File(in).exists()) {
      return false;
    }

    try {
      FileOutputStream fos = context.openFileOutput(out,
          Context.MODE_WORLD_READABLE);
      copyFile(new FileInputStream(in), fos);
      return true;
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return false;
  }

  public static final int REPORT_TYPE_START = 0;
  public static final int REPORT_TYPE_OVER = 1;
  public static final int REPORT_TYPE_CANCEL = 2;
  public static final int REPORT_TYPE_HAND_RETRY = 3;

  /**
   * 提交下载行为统计报告
   * 
   * @param reportType
   *          0 -- 下载开始 1 -- 下载完成 2 -- 下载取消 3 -- 手动重试下载
   */
  public static void submitDownloadLog(Context context, int reportType,
      int src, String uri, String ip, String pkgName, long size) {

    try {
      String source = "0";
      if (src == com.mol.market.common.download.Constants.DOWNLOAD_FROM_MARKET) {
        source = "0";
      } else if (src == com.mol.market.common.download.Constants.DOWNLOAD_FROM_CLOUD) {
        source = "p";
      } else if (src == com.mol.market.common.download.Constants.DOWNLOAD_FROM_BBS) {
        source = "b";
      } else if (src == com.mol.market.common.download.Constants.DOWNLOAD_FROM_OTA) {
        source = "o";
      }

      String networkStatus = "";
      if (Helper.isNetworkAvailable(context)) {
        int network = Helper.getActiveNetworkType(context);
        if (network == ConnectivityManager.TYPE_WIFI) {
          networkStatus = "network is wifi";
        } else if (network == ConnectivityManager.TYPE_MOBILE) {
          networkStatus = "network is mobile [Carrier 2.0]->"
              + getCarrier(context);
        }
      } else {
        networkStatus = "network is not available";
      }
      MarketAPI.submitDownloadLog(context, null, "", source, uri, ip,
              networkStatus, reportType, pkgName, size);

    } catch (Exception e) {
      Utils.E("Exception when submit download log", e);
    }
  }

  /**
   * 前往登录页面
   */
  public static void gotoLogin(Activity ctx) {
    Intent loginIntent = new Intent(ctx, LoginActivity.class);
    ctx.startActivityForResult(loginIntent, 0);
  }

  /**
   * 前往达人详细页面
   */
  public static void gotoMaster(Activity ctx, RecommendTopic topic) {
    Intent i = new Intent(ctx, RecommendActivity.class);
    i.putExtra(Constants.EXTRA_RECOMMEND_DETAIL, topic);
    ctx.startActivityForResult(i, 0);
  }

  /**
   * 前往产品详细页面
   */
  public static void gotoProductDetail(Activity ctx, String name, String pkgName) {
    Intent detailIntent = new Intent(ctx, ProductDetailActivity.class);
    detailIntent.putExtra(Constants.EXTRA_PRODUCT_NAME, name);
    detailIntent.putExtra(Constants.EXTRA_PACKAGE_NAME, pkgName);
    ctx.startActivity(detailIntent);
  }

  /**
   * 跳转到下一个页面
   * 
   * @param isForward(if true,can't back)
   *          是否转发(不能返回上一个界面)
   */
    public static void jumpToPage(Activity currentPage, Class<? extends Activity> nextPage,
            boolean isForward) {
        jumpToPage(currentPage, nextPage, isForward, null, null);
    }

  /**
   * 跳转到下一个界面.<br>
   * notice:当你在另一个Activity想要接收到传递的value时，<br>
   * 你可以使用getXXXExtra,也可以使用getSerializableExtra.<br>
   * 两者的区别在于是需要系统帮你向下转型还是自己手动向下转型.<br>
   *
   * @param ctx {@link Activity} should be use "this"
   * @param nextPage {@link Class} the target activity
   * @param isForward if true,can't back
   * @param key
   * @param value
   */
  public static void jumpToPage(Activity ctx, Class<? extends Activity>
      nextPage, boolean isForward, String[] key, Serializable[] value) {

    Intent intent = new Intent(ctx.getApplicationContext(), nextPage);
    //add value
    if (key != null && value != null && key.length == value.length) {
      for (int i = 0; i < key.length; i++) {
        intent.putExtra(key[i], value[i]);
      }
    }
    //set forward
    if (isForward) {
        ctx.finish();
    }
    //start
    ctx.startActivity(intent);
  }
  
  @SuppressWarnings("rawtypes")
  public static <T> boolean checkEmpty(T reference) {
    
    if (reference == null) {
      return true;
    }
    
    if (reference instanceof Map) {
      
      return ((Map)reference).size() == 0;
      
    } else if(reference instanceof List) {
      
      return ((List)reference).size() == 0;
      
    } else if(reference instanceof Set) {
      
      return ((Set)reference).size() == 0;
      
    }
    return false;
  }

  /**
   * 将Map转换成达人推荐Object
   */
  public static RecommendTopic mapToTopic(HashMap<String, Object> item) {
    RecommendTopic topic = new RecommendTopic();
    topic.id = (String) item.get(Constants.KEY_ID);
    topic.icon = (String) item.get(Constants.KEY_ICON_URL);
    topic.title = (String) item.get(Constants.KEY_RECOMMEND_NAME);
    topic.description = (String) item.get(Constants.KEY_DESCRIPTION);
    topic.up = Utils.getInt((String) item.get(Constants.KEY_LIKE));
    topic.down = Utils.getInt((String) item.get(Constants.KEY_DISLIKE));
    topic.experience = (String) item.get(Constants.KEY_EXPERIENCE);
    topic.user = (String) item.get(Constants.KEY_USER);
    topic.fans = Utils.getInt((String) item.get(Constants.KEY_FANS));
    return topic;
  }

  /**
   * 分享应用
   */
  public static void share(Context ctx, String name, String id) {
    Intent shareIntent = new Intent(Intent.ACTION_SEND);
    shareIntent.setType("text/plain");
    String content1 = String.format("我从机锋市场下载了“%s”，"
        + "这里有超过近10万的免费Android软件和游戏，快来一起玩吧。", name);
    String content2 = String.format("http://apk.gfan.com/Product/App%s.html",
        id);
    shareIntent
        .putExtra(android.content.Intent.EXTRA_TEXT, content1 + content2);
    ctx.startActivity(Intent.createChooser(shareIntent, "分享"));
  }

  /**
   * 是否有硬键盘
   */
  public static boolean isKeyboardAvailable(Context context) {
    return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
  }
  
  /**
   * 将分转换成元
   */
  public static String convertCentToYuan(String cent) {
    return convertCentToYuan(getInt(cent));
  }
  
  /**
   * 将分转换成元
   */
  public static String convertCentToYuan(int cent) {
    float centValue = cent / 100f;
    return String.format("%.2f", centValue);
  }

  /**
   * format一个String
   */
  public static CharSequence formatByColor(String data, String format, int Color) {

    if (data == null || format == null) {
      return null;
    }

    String finalData = String.format(format, data);
    int start = finalData.indexOf(data);
    int end = finalData.length();

    if (start == -1) {
      //the specified string is not a substring.
      return null;
    }
    SpannableStringBuilder style = new SpannableStringBuilder(finalData);
    style.setSpan(new ForegroundColorSpan(Color), start, end,
        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    return style;
  }

  /**
   * 从ArrayList<HashMap<String,Object>>取出特定的值.
   *
   * @param <T>    取出值的类型
   * @param result
   * @param pos
   * @param key
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <T> T getValue(ArrayList<HashMap<String, Object>> result,
      int pos, String key) {

    if (result == null || result.size() <= pos || result.get(pos) == null
        || result.get(pos).get(key) == null) {
      throw new IllegalArgumentException("invalid arguments");
    }
    return (T) result.get(pos).get(key);
  }

  /**
   * 关闭流的工具方法，不抛出任何异常。
   * @param stream IO流
   */
  public static void closeStreamSilently(Closeable stream) {

    if (stream == null) {
      return;
    }

    try {
      stream.close();
    } catch (IOException e) {
      Utils.D("Error when close stream", e);
    }
  }

  /**
   * 创建桌面快捷方式
   *
   * @param curActivity
   * @param name
   * @param drawableId
   * @param target
   */
  public static void createDesktopIcon(final Activity curActivity, String name,
      int drawableId, Class<? extends Activity> target) {
    // 创建快捷方式的Intent
    Intent shortcutIntent = new Intent(
        "com.android.launcher.action.INSTALL_SHORTCUT");
    // 不允许重复创建
    shortcutIntent.putExtra("duplicate", true);
    // 快捷方式名称
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME,
        name);
    // 快捷方式图片
    Parcelable icon = Intent.ShortcutIconResource.fromContext(
        curActivity.getApplicationContext(), drawableId);
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
    // 点击快捷图片，运行的程序主入口
    Intent intent = new Intent(Intent.ACTION_MAIN).setComponent(
        new ComponentName(curActivity, SplashActivity.class));
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
    // 发送广播。OK
    curActivity.sendBroadcast(shortcutIntent);
  }

    /**
     * Get the response charset
     */
    public static String getContentCharSet(final HttpURLConnection connection) {

        if (connection == null) {
            throw new IllegalArgumentException("HTTP entity may not be null");
        }
        String charset = null;
        String contentType = connection.getContentType();
        if (contentType != null) {
            String[] values = contentType.split(";");
            if (values.length > 0) {
                for (String value : values) {
                    if (value.trim().toLowerCase().startsWith("charset=")) {
                        charset = value.substring("charset=".length());
                        break;
                    }
                }
            }
        }
        return charset;
    }

    /**
     * Convert input stream to string
     * @throws IOException
     */
    public static String convertToString(InputStream input, String charset)
            throws IOException {

        if (TextUtils.isEmpty(charset)) {
            charset = ENCODING_UTF8;
        }
        Reader reader = new InputStreamReader(input, charset);
        StringBuilder buffer = new StringBuilder();
        try {
            char[] tmp = new char[1024];
            int l;
            while((l = reader.read(tmp)) != -1) {
                buffer.append(tmp, 0, l);
            }
        } finally {
            reader.close();
        }
        return buffer.toString();
    }

    /**
     * 确保你的方法一定会运行在Ui线程中。
     * <hr>
     * 不要在此方法中进行大量耗时运算，否则将会阻塞ui线程。
     * {@link Activity#runOnUiThread(Runnable)}
     */
    public static void runOnUiThread(Runnable what){
        if(Looper.myLooper() == null || Looper.myLooper() != Looper.getMainLooper()){
            new Handler().post(what);
        }else{
            what.run();
        }
    }
}